/****************************************\
|* MFZPSP LLE PSP Emulator              *|
|* Copyright (c) 2008 Mauro Frischherz  *|
|* See License.txt for details          *|
\****************************************/


/*Includes*/
#include "../global.h"
#include "../tools/log.h"
#include "../types.h"
#include "cpu.h"
#include "memory.h"
#include "gpu.h"
#include "math.h"

/* Multi-System stuff */
#ifdef GUI_WINDOWS

#include <process.h>
uintptr_t CPUThread;

#elif GUI_XLINUX

#include <pthread.h>
pthread_t CPUThread;

#elif GUI_OSX

#include <pthread.h>
pthread_t CPUThread;

#endif

/* Functionheads */
void Reset();
void* CPULoop();
void SysCall(uint Code);

/* Declarations */
uint OpCode;
uint PC, nPC;

int CPUState = CPU_STATE_STOPPED;
bool StopNeeded = false;
bool CPUStopped = true;

bool Threading = true;

// CPU Stuff
uint  regs[32];
uint HI, LO;
uint CC;

// CP0 Stuff

// CP1 Stuff
float regsf[32];

// CP2 Stuff



//////////////////////////////////////////////////////////////////////////////////////////
// CPU-Specific Operations_____________________________________________________________ //
//////////////////////////////////////////////////////////////////////////////////////////

/* General CPU Macros */
#define IMM 	(short)(OpCode & 0xFFFF)
#define IMMu 	(ushort)(OpCode & 0xFFFF)
#define base 	(uint)regs[OpCode >> 21 & 0x1F]
#define sa 		((OpCode >> 6) & 0x1F)
#define code 	((OpCode >> 6) & 0x14)

#define DIV(a, b) { LO = a / b; HI = a % b; }

/* Macros to access the right Registers */
#define rs regs[OpCode >> 21 & 0x1F]
#define rt regs[OpCode >> 16 & 0x1F]
#define rd regs[OpCode >> 11 & 0x1F]

#define rsu (uint)regs[OpCode >> 21 & 0x1F]
#define rtu (uint)regs[OpCode >> 16 & 0x1F]
#define rdu (uint)regs[OpCode >> 11 & 0x1F]

/* CoProcessor1 Macros */
#define fmt (uint)regs[OpCode >> 21 & 0x1F]
#define co	(bool)((OpCode >> 25) & 0x01);

#define ft regsf[OpCode >> 16 & 0x1F]
#define fs regsf[OpCode >> 11 & 0x1F]
#define fd regsf[OpCode >> 06 & 0x1F]

#define ftd (double)regsf[OpCode >> 16 & 0x1F]
#define fsd (double)regsf[OpCode >> 11 & 0x1F]
#define fdd (double)regsf[OpCode >> 06 & 0x1F]

/* We'll need some inline-asm functions */
#define MULT(a, b) { asm ("movl %2, %%eax; movl %3, %%ebx; imul %%ebx; movl %%eax, %1; movl %%edx, %0;" : "=r"(HI), "=r"(LO) : "r"(a), "r"(b) : "%eax", "%ebx", "%edx"); }
#define MULTU(a, b) { asm ("movl %2, %%eax; movl %3, %%ebx; mul %%ebx; movl %%eax, %1; movl %%edx, %0;" : "=r"(HI), "=r"(LO) : "r"(a), "r"(b) : "%eax", "%ebx", "%edx"); }

inline int SLL(int a, int b) { int ret = 0; asm ("movl %1, %%eax; movl %2, %%ecx; shll %%cl, %%eax; movl %%eax, %0;" : "=r"(ret) : "r"(a), "r"(b) : "%eax", "%ecx"); return ret;}
inline int SRL(int a, int b) { int ret = 0; asm ("movl %1, %%eax; movl %2, %%ecx; shrl %%cl, %%eax; movl %%eax, %0;" : "=r"(ret) : "r"(a), "r"(b) : "%eax", "%ecx"); return ret;}
inline int SRA(int a, int b) { int ret = 0; asm ("movl %1, %%eax; movl %2, %%ecx; sarl %%cl, %%eax; movl %%eax, %0;" : "=r"(ret) : "r"(a), "r"(b) : "%eax", "%ecx"); return ret;}

//////////////////////////////////////////////////////////////////////////////////////////
// _____________________________________________________________CPU-Specific Operations //
//////////////////////////////////////////////////////////////////////////////////////////

int GetCPUState() {	return CPUState; }
uint GetPC() { return PC; }
uint GetReg(uint Index)
{
	if(Index < 32)
		return regs[Index];
	else
		return 0;
}

uint GetOp(uint PC)
{
	return uRead32(PC);
}

// Resets CPU Values to 0
void ResetCPU()
{
	PC = HI = LO = CC = 0;
	PC = 0x08900000;
	nPC = PC + 4;
	CPUStopped = true;
	StopNeeded = false;
	Threading = true;

	int i;
	for(i = 0; i < 32; i++)
	{
		regs[i] = 0;
		regsf[i] = 0;
	}
}

// Resets CPU and sets State to Stopped
void StopCPU()
{
	StopNeeded = true;
	Threading = true;
	while(!CPUStopped) {}
	
	CPUState = CPU_STATE_STOPPED;
	
	ResetCPU();
}

// Pauses CPU
void PauseCPU()
{
	StopNeeded = true;
	while(!CPUStopped) {}
	
	CPUState = CPU_STATE_PAUSED;
}

// Stops CPU resets values to 0
void Reset()
{
	StopCPU();
	ResetCPU();
}

// Creates the CPU-Thread and prints debug info
int RunCPU()
{	
	if(!CPUState == CPU_STATE_RUNNING || CPUState == CPU_STATE_PAUSED || CPUState == CPU_STATE_STOPPED)
	{
		#ifdef GUI_WINDOWS
		
		if(Threading)
			CPUThread = (uintptr_t)_beginthread((void*)CPULoop, 0, NULL);
		
		#elif GUI_XLINUX
		
		pthread_create(&CPUThread, NULL, CPULoop, NULL);
		
		#elif GUI_OSX
		
		pthread_create(&CPUThread, NULL, CPULoop, NULL);
		
		#endif
		
		if(Threading)
		{
			if(!CPUThread)
			{
				printerror("Could not create CPU thread.");
				CPUState = CPU_STATE_STOPPED;
			}
			else
			{
				printevent("CPU-Thread created successfully.");
				CPUState = CPU_STATE_RUNNING;
			}
		}
		else
		{
			CPULoop();
		}
	}
	else
		printerror("CPU already running");
	
	return (int)CPUThread;
}

void* CPULoop()
{
	CPUStopped = false;
	for(;;)
	{
		OpCode = uRead32(PC);

		switch(OpCode >> 26) // Last 6 Bits only
		{
/*____*/case 0x00000000: // R Type Instructions
			switch(OpCode & 0x3F){
/* ADD 		*/	case 0x00000020: rd = rs + rt; PC = nPC; nPC += 4; break;
/* ADDU 	*/	case 0x00000021: rd = rs + rt; PC = nPC; nPC += 4; break;
/* AND 		*/	case 0x00000024: rd = rs & rt; PC = nPC; nPC += 4; break;
/* BREAK 	*/	case 0x0000000D: PC = nPC; nPC += 4; PauseCPU(); break;
/* DIV 		*/	case 0x0000001A: if(rt > 0) DIV(rs, rt); PC = nPC; nPC += 4; break;
/* DIVU 	*/	case 0x0000001B: if(rt > 0) DIV(rsu, rtu); PC = nPC; nPC += 4; break;
/* JALR 	*/	case 0x00000009: PC = nPC; nPC = rs; rd = PC + 4; break;
/* JR 		*/	case 0x00000008: PC = nPC; nPC = rs; break;
/* MFHI 	*/	case 0x00000010: rd = HI; PC = nPC; nPC += 4; break;
/* MFLO 	*/	case 0x00000012: rd = LO; PC = nPC; nPC += 4; break;
/* MTHI 	*/	case 0x00000011: HI = rs; PC = nPC; nPC += 4; break;
/* MTLO 	*/	case 0x00000013: LO = rs; PC = nPC; nPC += 4; break;
/* MULT 	*/	case 0x00000018: MULT(rs, rt); PC = nPC; nPC += 4; break;
/* MULTU 	*/	case 0x00000019: MULTU(rsu, rtu); PC = nPC; nPC += 4; break;
/* NOR 		*/	case 0x00000027: rd = ~(rs | rt); PC = nPC; nPC += 4; break;
/* OR 		*/	case 0x00000025: rd = (rs | rt); PC = nPC; nPC += 4; break;
/* SLL		*/	case 0x00000000: rd = SLL(rtu, sa); PC = nPC; nPC += 4; break;
/* SLLV		*/	case 0x00000004: rd = SLL(rtu, (rsu & 0x1F)); PC = nPC; nPC += 4; break;
/* SLT		*/	case 0x0000002A: rd = ((rs < rt) ? 1 : 0); PC = nPC; nPC += 4; break;
/* SLTU		*/	case 0x0000002B: rd = ((rsu < rtu) ? 1 : 0); PC = nPC; nPC += 4; break;	
/* SRA		*/	case 0x00000003: rd = SRA(rt, sa); PC = nPC; nPC += 4; break;
/* SRAV		*/	case 0x00000007: rd = SRA(rt, (rsu & 0x1F)); PC = nPC; nPC += 4; break;
/* SRL		*/	case 0x00000002: rd = SRL(rtu, (uint)sa); PC = nPC; nPC += 4; break;
/* SRLV		*/	case 0x00000006: rd = SRL(rtu, (rsu & 0x1F)); PC = nPC; nPC += 4; break;
/* SUB		*/	case 0x00000022: rd = rs - rt; PC = nPC; nPC += 4; break;
/* SUBU		*/	case 0x00000023: rd = rsu - rtu; PC = nPC; nPC += 4; break;
/* SYNC		*/	case 0x0000000F: printunknowninst(OpCode); break;
/* SYSCALL	*/	case 0x0000000C: PC = nPC; nPC += 4; PauseCPU(); SysCall(code); break; // For Now CPU Pause on SysCall!
/* TEQ		*/	case 0x00000034: PC = nPC; nPC += 4; if(rt == rs) PauseCPU(); break;
/* TGE		*/	case 0x00000030: PC = nPC; nPC += 4; if(rt <= rs) PauseCPU(); break;
/* TGEU		*/	case 0x00000031: PC = nPC; nPC += 4; if(rtu <= rsu) PauseCPU(); break;
/* TLT		*/	case 0x00000032: PC = nPC; nPC += 4; if(rt > rs) PauseCPU(); break;
/* TLTU		*/	case 0x00000033: PC = nPC; nPC += 4; if(rtu > rsu) PauseCPU(); break;
/* TNE		*/	case 0x00000036: PC = nPC; nPC += 4; if(rt != rs) PauseCPU(); break;
/* XOR		*/	case 0x00000026: rd = (rt ^ rs); PC = nPC; nPC += 4; break;
/* UNKNOWN	*/	default: printunknowninst(OpCode); PC = nPC; nPC += 4;break;
			}
	    	break;
/*JUMP*/case 0x00000002: PC = nPC; nPC = (PC & 0xF0000000) | (OpCode & 0x3FFFFFF << 2); break; // Jump
/*_JAL*/case 0x00000003: PC = nPC; regs[31] = PC + 4; nPC = (PC & 0xF0000000) | (OpCode & 0x3FFFFFF << 2); break; // Jump and Link
/*____*/case 0x00000010: // CoProcessor 0
			switch(OpCode >> 21) {
/* ERET 	*/
/* BC0F 	*/
/* BC0FL 	*/
/* BC0T 	*/
/* BC0TL 	*/
/* CTC0 	*/
/* LWC0 	*/
/* MFC0 	*/
/* MTC0 	*/
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;
			}
		break;
/*____*/case 0x00000011: // CoProcessor 1 FPU 
			if((OpCode >> 24) == 0x45) 
				switch(OpCode & 0x001F0000) { 
/* BC1F		*/	case 0x00000000: PC = nPC; nPC = ((!CC) ? (IMM << 2) : 4); break;
/* BC1FL 	*/	case 0x00030000: if(!CC) { PC = nPC; nPC = (IMM << 2); } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BC1T 	*/	case 0x00010000: PC = nPC; nPC = (CC ? (IMM << 2) : 4); break;
/* BC1TL 	*/	case 0x00040000: if(CC) { PC = nPC; nPC = (IMM <<2); } else { PC = nPC + 4; nPC = PC + 4; } break;
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;
				}
			else if((OpCode) < 0x460000FF)
				switch(OpCode & 0xFFE0003F) { // This Mask includes fmt (is single precision only)
/* CFC1 	*/	case 0x44400000: rt = (int)fs; PC = nPC; nPC += 4; break;
/* CTC1 	*/	case 0x44C00000: CC = rt; PC = nPC; nPC += 4; break;
/* MFC1 	*/	case 0x44000000: rt = (int)fs; PC = nPC; nPC += 4; break;
/* MTC1 	*/	case 0x44800000: fs = rt; PC = nPC; nPC += 4; break;
/* ABS.S	*/	case 0x46000005: fd = ((fs < 0) ? -fs : fs); PC = nPC; nPC += 4; break;
/* ADD.S	*/	case 0x46000000: fd = fs + ft; PC = nPC; nPC += 4; break;
/* C.UN.S	*/	case 0x46000031: CC = (isnan(fs) || isnan(ft)); PC = nPC; nPC += 4; break;
/* C.LT.S	*/	case 0x4600003C: CC = (fs < ft); if(isnan(fs) || isnan(ft)) ( CC = !CC ); PC = nPC; nPC += 4; break;
/* C.LE.S	*/	case 0x4600003E: CC = (fs <= ft); if(isnan(fs) || isnan(ft)) ( CC = !CC ); PC = nPC; nPC += 4; break;
/* C.EQ.S	*/	case 0x46000033: CC = (fs != ft); PC = nPC; nPC += 4; break;
/* C.UEQ.S	*/	case 0x46000032: CC = (fs == ft); PC = nPC; nPC += 4; break;
/* CEIL.W.S	*/	case 0x4600000E: fd = fs; PC = nPC; nPC += 4; break;		// Losing precision
/* CVT.S.W	*/	case 0x46000020: fd = fs; PC = nPC; nPC += 4; break;		// Losing precision
/* DIV.S	*/	case 0x46000003: fd = fs / ft; PC = nPC; nPC += 4; break;
/* MOV.S	*/	case 0x46000006: fd = fs; PC = nPC; nPC += 4; break;
/* MUL.S	*/	case 0x46000002: fd = fs * ft; PC = nPC; nPC += 4; break;
/* NEG.S	*/	case 0x46000007: fd = -fs; PC = nPC; nPC += 4; break;
/* RND.W.S	*/	case 0x4600000C: printunknowninst(OpCode); break;			// Fix this
/* SQRT.S	*/	case 0x46000004: fd = sqrt(fs); PC = nPC; nPC += 4; break;
/* SUB.S	*/	case 0x46000001: fd = fs - ft; PC = nPC; nPC += 4; break;
/* TRC.W.S	*/	case 0x4600000D: printunknowninst(OpCode); break;			// Fix this
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;
				}
			else
				switch((OpCode >> 26) & 0x3F) {
/* LWC1 	*/	case 0x31: ft = Read32(IMM + base); PC = nPC; nPC += 4; break;
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;				
				}
			break;
/*____*/case 0x00000013: // CoProcessor 2 VFPU
			switch(OpCode >> 21) {
/* BC2F 	*/
/* BC2FL 	*/
/* BC2T 	*/
/* BC2TL 	*/
/* CFC2 	*/
/* CTC2 	*/
/* LWC2 	*/
/* MFC2 	*/
/* MTC2 	*/
/* SWC2		*/
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;
			}
			break;
/*____*/case 0x00000001: // REGIMM Branches
			switch((OpCode >> 16) & 0x1F) {
/* BGEZ 	*/	case 0x00000001: PC = nPC; nPC += ((rs >= 0) ? IMM << 2 : 4); break;
/* BGEZAL 	*/	case 0x00000011: PC = nPC; regs[31] = uRead32(PC + 4); nPC += ((rs >= 0) ? IMM << 2 : 4); break;
/* BGEZALL 	*/	case 0x00000013: if(rs >= 0) { PC = nPC; regs[31] = uRead32(nPC + 4); nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BGEZL 	*/	case 0x00000003: if(rs >= 0) { PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BLTZ 	*/	case 0x00000000: PC = nPC; nPC += ((rs < 0) ? IMM << 2 : 4); break;
/* BLTZAL 	*/	case 0x00000010: PC = nPC; regs[31] = uRead32(nPC + 4); nPC += ((rs < 0) ? IMM << 2 : 4); break;
/* BLTZALL 	*/	case 0x00000012: if(rs < 0) { PC = nPC; regs[31] = uRead32(nPC + 4); nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BLTZL 	*/	case 0x00000002: if(rs < 0) { PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* TEQI		*/	case 0x0000000C: PC = nPC; nPC += 4; if(rs == IMM) PauseCPU(); break;
/* TGEI		*/	case 0x00000008: PC = nPC; nPC += 4; if(rs >= IMM) PauseCPU(); break;
/* TGEIU	*/	case 0x00000009: PC = nPC; nPC += 4; if(rsu >= IMMu) PauseCPU(); break;
/* TLTI		*/	case 0x0000000A: PC = nPC; nPC += 4; if(rs < IMM) PauseCPU(); break;
/* TLTIU	*/	case 0x0000000B: PC = nPC; nPC += 4; if(rsu < IMMu) PauseCPU(); break;
/* TNEI		*/	case 0x0000000E: PC = nPC; nPC += 4; if(rs != IMM) PauseCPU(); break;
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;

			}
			break;
/*____*/default:   // I Type Instructions
			switch(OpCode >> 26){
/* ADDI 	*/	case 0x00000008: rt = rs + IMM; PC = nPC; nPC += 4; break;
/* ADDIU 	*/	case 0x00000009: rt = rs + IMM; PC = nPC; nPC += 4; break;
/* ANDI 	*/	case 0x0000000C: rt = rs & IMM; PC = nPC; nPC += 4; break;
/* BEQ 		*/	case 0x00000004: PC = nPC; nPC += ((rs == rt) ? IMM << 2 : 4); break;
/* BEQL 	*/	case 0x00000014: if(rs == rt) { PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BGTZ 	*/	case 0x00000007: PC = nPC; nPC += ((rs > 0) ? IMM << 2 : 4); break;
/* BGTZL 	*/	case 0x00000017: if(rs > 0) 	{ PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BLEZ 	*/	case 0x00000006: PC = nPC; nPC += ((rs <= 0) ? IMM << 2 : 4); break;
/* BLEZL 	*/	case 0x00000016: if(rs <= 0) 	{ PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* BNE 		*/	case 0x00000005: PC = nPC; nPC += ((rs != rt) ? IMM << 2 : 4); break;
/* BNEL 	*/	case 0x00000015: if(rs != rt) { PC = nPC; nPC += IMM << 2; } else { PC = nPC + 4; nPC = PC + 4; } break;
/* CACHE 	*/	case 0x0000002F: printunknowninst(OpCode); break;
/* LB 		*/	case 0x00000020: rt = Read8(base + IMM); PC = nPC; nPC += 4; break;
/* LBU 		*/	case 0x00000024: rt = uRead8(base + IMM); PC = nPC; nPC += 4; break;
/* LH 		*/	case 0x00000021: rt = Read16(base + IMM); PC = nPC; nPC += 4; break;
/* LHU 		*/	case 0x00000025: rt = uRead16(base + IMM); PC = nPC; nPC += 4; break;
/* LL 		*/	case 0x00000030: printunknowninst(OpCode); break;
/* LUI 		*/	case 0x0000000F: rt = IMM << 16; PC = nPC; nPC += 4; break;
/* LW 		*/	case 0x00000023: rt = Read32(base + IMM); PC = nPC; nPC += 4; break;
/* LWL 		*/	case 0x00000022: rt = (rt & 0x0000FFFF) | (Read32(base + IMM) & 0xFFFF0000); PC = nPC; nPC += 4; break; // NOT SURE HERE
/* LWR 		*/	case 0x00000026: rt = (rt & 0xFFFF0000) | (Read32(base + IMM) & 0x0000FFFF); PC = nPC; nPC += 4; break; // NOT SURE HERE
/* LWU 		*/	case 0x00000027: rt = uRead32(base + IMM); PC = nPC; nPC += 4; break;
/* ORI 		*/	case 0x0000000D: rt = (rs | IMMu); PC = nPC; nPC += 4; break;
/* SB		*/	case 0x00000028: Write8(base + IMM, rt); PC = nPC; nPC += 4; break;
/* SC		*/	case 0x00000038: printunknowninst(OpCode); break;
/* SH		*/	case 0x00000029: Write16(base + IMM, rt); PC = nPC; nPC +=4; break;
/* SLTI		*/	case 0x0000000A: rt = ((rs < IMM) ? 1 : 0); PC = nPC; nPC += 4; break;
/* SLTIU	*/	case 0x0000000B: rt = ((rsu < IMMu) ? 1 : 0); PC = nPC; nPC += 4; break;
/* SW		*/	case 0x0000002B: Write32(base + IMM, rt); PC = nPC; nPC += 4; break;
/* SWL		*/	case 0x0000002A: Write16(base + IMM, ((rt >> 16) & 0xFFFF)); PC = nPC; nPC += 4; break;	// NOT SURE HERE
/* SWR		*/	case 0x0000002E: Write16(base + IMM, (rt & 0xFFFF)); PC = nPC; nPC += 4; break;			// NOT SURE HERE
/* XORI		*/	case 0x0000000E: rt = (rs ^ IMMu); PC = nPC; nPC += 4; break;
/* UNKNOWN	*/	default: printunknowninst(OpCode); break;
			}
			break;
		}

		if(StopNeeded)
		{
			CPUStopped = true;
			StopNeeded = false;
			break;
		}
	}
	return NULL;
}

// This won't work yet
void SysCall(uint Code)
{
	printsyscall(Code);
	RunCPU();
}


bool SingleStep()
{
	if(!(CPUState == CPU_STATE_PAUSED || CPUState == CPU_STATE_STOPPED))
	{
		printerror("CPU must be paused for single-step debugging");
		return false;
	}
	else
	{
		Threading = false;
		StopNeeded = true;
		RunCPU();
		CPUState = CPU_STATE_PAUSED;
		Threading = true;
		return true;
	}
}

void SetEntry(uint Entry)
{
	PC = Entry;
	nPC = PC + 4;
}
